/**
 * @file 请求字符串的解析和构建方法集合，包括字符的编码、解码等方法。
 * @module ke-url/query
 * @exports ke-url/query
 * @requires lodash
 * @requires ke-url/options
 */

import * as _ from 'lodash';
import {debug, DebugLevel} from './options';

/**
 * 对字符进行编码处理（RFC 3986），该函数返回的结果与php的rawurlencode符合。
 *
 * 该方法会尝试兼容在编码过程中碰到的错误，当抛出异常，会返回一个空字符串，而不中断函数的执行。
 *
 * @memberOf module:ke-url/query
 * @param {string} value 需要进行编码处理的值。
 * @returns {string} 经过编码处理的结果。
 * @see {@link http://php.net/manual/en/function.rawurlencode.php}
 */
export function encode(value): string {
	try {
		value = (typeof value === 'undefined' || value === null) ? '' : value + '';
		return encodeURIComponent(value).replace(/[!'()*]/g, function(c) {
			return '%' + c.charCodeAt(0).toString(16);
		});
	}
	catch (e) {
		debug(DebugLevel.Error, 'encoding', value, 'error occurred :', e);
		return '';
	}
}

/**
 * 请求字符串解码，该函数返回的结果与php的rawurldecode符合，并额外将 `+` 转换 `%20` 空格。
 *
 * ```
 * decode('aa+bb+cc');     // 'aa bb cc'
 * decode('aa%20bb%20cc'); // 'aa bb cc'
 * ```
 *
 * @memberOf module:ke-url/query
 * @param {string} value 需要解码处理的值得。
 * @returns {string} 返回解码后的结果
 * @see {@link http://php.net/manual/en/function.rawurldecode.php}
 */
export function decode(value): string {
	try {
		value = (typeof value === 'undefined' || value === null) ? '' : value + '';
		value = decodeURIComponent(value.replace(/%(?![\da-f]{2})/gi, function () {
			// PHP tolerates poorly formed escape sequences
			return '%25';
		}).replace(/\+/g, '%20'));
		return value;
	} catch(e) {
		debug(DebugLevel.Error, 'decoding', value, 'error occurred :', e);
		return '';
	}
}

const DEFAULT_SEPARATOR = '&';
const DEFAULT_EQUAL_SIGN = '=';

// 这里输出，主要为了方便typescript引用使用
export interface QueryObject {
	[any: string]: any;
}

/**
 * 请求字符串的数据结构接口。
 *
 * @memberOf module:ke-url/query
 * @interface QueryObject
 */

function filterPrefix(prefix: string): string[] {
	let regexp = /([^\]\[]+|\[([^.[\]]*)\])/g;
	let keysAry = [];
	(prefix + '').replace(regexp, ($0, $1, args) => {
		// ["a"], ['a'] => a
		let item = _.trim($1, '[]"\' ');
		keysAry.push(item);
		return '';
	});
	return keysAry;
}

/**
 * 将请求字符串解析为一个对象的结构。
 *
 * 以下为较为特殊的字符串的结果匹配：
 * ```
 * ok=====1 => ok='1'
 * a=b=c=1 => a='1'
 * a=b=c= => a='' (空字符)
 * a['b'] 或 a["b"] => a[b]
 * ```
 *
 * @memberOf module:ke-url/query
 * @param {string} query  输入的请求字符串，必须是字符格式。如果该字符串以?开始，会过自行滤掉。
 * @param {QueryObject} result 允许指定一个既有的result，并将当前的query写入到result中。
 * @param {Function} filter 过滤函数，允许传入一个函数进行进一步的值过滤（每个片段的值，且基于已经rawurldecode的值）。
 * @returns {QueryObject}
 */
export function parseQuery(query: string, result?: QueryObject, filter?: Function): QueryObject {
	if (!result)
		result = {};
	// 只处理字符串格式
	if (!_.isString(query)) {
		if (_.isObject(query))
			return <QueryObject>query;
		return result;
	}
	// 过滤?
	query = _.trimStart(query, '?');
	let sep = DEFAULT_SEPARATOR;
	let eq = DEFAULT_EQUAL_SIGN;
	let pairs = query.split(sep);

	if (!pairs.length)
		return result;

	_.forEach(pairs, function (pair) {
		if (!pair)
			return;

		let target = result;
		let keys: string, value: string;

		// ok=====1 => ok=1
		pair = pair.replace(/[=]+/, '=');

		let split = pair.split(eq);
		keys = split.shift();

		if (!keys) // &=a 未知的key
			return;

		// a=b=c=1 => a=1
		value = split.pop() || ''; // &ok || &ok= 无value

		keys = decode(keys);

		if (value)
			value = decode(value);

		if (filter && _.isFunction(filter)) {
			value = filter(value);
			if (typeof value === 'undefined')
				value = null;
		}

		let keysAry = filterPrefix(keys);
		// let regexp = /([^\]\[]+|\[([^.[\]]*)\])/g;
		// let keysAry = [];
		// keys.replace(regexp, ($0, $1, args) => {
		// 	// ["a"], ['a'] => a
		// 	keysAry.push(_.trim($1, '[]"\' '));
		// 	return '';
		// });
		let keysDepth = keysAry.length;

		if (keysDepth < 1)
			return;

		let lastKey = keysAry.pop();

		if (keysDepth > 1) {
			_.forEach(keysAry, (key, i) => {
				if (key === '')
					key = _.size(target) + '';
				if (!_.isObject(target[key]))
					target[key] = {};
				target = target[key];
			});
		}

		if (lastKey === '')
			lastKey = _.size(target) + '';

		target[lastKey] = value;
	});

	return <QueryObject>result;
}

/**
 * 将一个数据结构（ `Object` 或 `Array` ）转换为请求字符串，并进行编码。
 *
 * @memberOf module:ke-url/query
 * @param query {Object|Array} 输入的请求数据结构，允许一个 Object 或 Array 结构。如果是 Array 结构，必须指定前缀 prefix
 * @param prefix {string} 指定的生成的
 * @returns {string} 返回的经过编码的字符串格式的请求字符串
 */
export function buildQuery(query, prefix: string = ''): string {
	let result = [];
	let sep = DEFAULT_SEPARATOR;
	let eq = DEFAULT_EQUAL_SIGN;
	if (!_.isObjectLike(query) || (_.isArray(query) && !prefix))
		return '';

	// @todo prefix应该也经过encode的处理
	_.forEach(query, (value, key) => {
		if (prefix)
			key = prefix + '[' + encode(key) + ']';
		else
			key = encode(key);
		let temp;
		if (_.isFunction(value))
			value = value(value);

		if (_.isArray(value)) {
			var qs = {};
			_.forEach(value, (val, index) => {
				qs[index] = val;
			});
			temp = buildQuery(qs, key);
		}
		else if (_.isObject(value)) {
			temp = buildQuery(value, key);
		}
		else if (value !== null) {
			if (value === true)
				value = '1';
			else if (value === false)
				value = '0';
			else
				value = _.toString(value);
			// key = rawurlencode(key);
			value = encode(value);
			if (value)
				temp = key + eq + value;
			else
				temp = key;
		}
		if (temp)
			result.push(temp);
	});
	return result.join(sep);
}
